package main

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
)

const (
	md5Subffix = ".md5"
)

var output string
var input string
var isinputfile bool

func init() {
	flag.StringVar(&output, "out", "", "Output hash `file`.")
	flag.StringVar(&input, "in", "", "Input a `string or filename`.")
	flag.BoolVar(&isinputfile, "f", false, "If in is file turn on.")

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s:\n", filepath.Base(os.Args[0]))
		flag.PrintDefaults()
	}
}

func md5Hash(data string) (hashValue string) {
	hash := md5.New()
	io.WriteString(hash, data)
	hashValue = hex.EncodeToString(hash.Sum(nil))
	return
}

func writeFile(fileName, data string) error {
	if file, err := os.Create(fileName); err == nil {
		defer file.Close()
		_, err = file.WriteString(data)
		return err
	} else {
		return errors.New("Create file " + fileName + " failed.")
	}
}

func readFile(fileName string) (string, error) {
	if file, err := os.Open(fileName); err == nil {
		defer file.Close()
		if data, err := ioutil.ReadAll(file); err == nil {
			return string(data), nil
		} else {
			return "", errors.New("Read file " + fileName + " failed.")
		}
	} else {
		return "", errors.New("Open file " + fileName + " failed.")
	}
}

func getFilePath(dir, fileName string) string {
	if !filepath.IsAbs(fileName) {
		fileName = filepath.Join(dir, fileName)
	}
	return fileName
}

func main() {
	flag.Parse()
	var currentDir = filepath.Dir(os.Args[0])
	if input != "" {
		if isinputfile {
			var inputFilePath = getFilePath(currentDir, input)
			if data, err := readFile(inputFilePath); err == nil {
				hashValue := md5Hash(data)
				var outputFilePath = inputFilePath + md5Subffix
				if output != "" {
					outputFilePath = getFilePath(currentDir, output) + md5Subffix
				}
				writeFile(outputFilePath, hashValue)
				fmt.Printf("Info: md5 content output to file : %s \n", outputFilePath)
				fmt.Printf("Info: md5(file(%s)) == %s \n", input, hashValue)
			} else {
				fmt.Println(err.Error())
				flag.Usage()
			}
		} else {
			hashValue := md5Hash(input)
			fmt.Printf("Info: md5(%s) == %s \n", input, hashValue)
		}
	}
	flag.Usage()
}
